package util.base;

import util.string.StringUtil;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 将数字金额转换为大写中文金额
 * @Author zt
 * @date 2021/11/11 11:33
 * @Version 1.0
 */
public class MoneyCastUtil {
    // 大写数字
    private static final String[] NUMBERS = {"零","壹","贰","叁","肆","伍","陆","柒","捌","玖"};
    // 整数部分的单位
    private static final String[] IUNIT = {"元","拾","佰","仟","万","拾","佰","仟","亿","拾","佰","仟","万","拾","佰","仟"};
    // 小数部分的单位
    private static final String[] DUNIT = {"角","分","厘"};

    /**
     * 汉语中数字大写
     */
    private static final String[] CN_UPPER_NUMBER = {"零","壹","贰","叁","肆","伍","陆","柒","捌","玖" };

    /**
     * 汉语中货币单位大写
     */
    private static final String[] CN_UPPER_MONETRAY_UNIT = { "分", "角", "元","拾", "佰", "仟", "万", "拾",
            "佰", "仟", "亿", "拾", "佰", "仟", "兆", "拾",
            "佰", "仟" };
    /**
     * 特殊字符：整
     */
    private static final String CN_FULL = "";

    /**
     * 特殊字符：负
     */
    private static final String CN_NEGATIVE = "负";
    /**
     * 零元整
     */
    private static final String CN_ZEOR_FULL = "零元整";

    /**
     * 金额的精度，默认值为2
     */
    private static final int MONEY_PRECISION = 2;


    /**
     *  转换为大写的中文金额
     * @param str 字符串类型的 金额数字
     * @return
     */
    public static String toChinese(String str) {
        // 判断输入的金额字符串是否符合要求
        if (StringUtil.isBlank(str) || !str.matches("(-)?[\\d]*(.)?[\\d]*")) {
            return "抱歉，请输入数字！";
        }

        if("0".equals(str) || "0.00".equals(str) || "0.0".equals(str)) {
            return "零元";
        }

        // 判断金额数字中是否存在负号"-"
        boolean flag = false;
        if(str.startsWith("-")){
            // 标志位，标志此金额数字为负数
            flag = true;
            str = str.replaceAll("-", "");
        }

        // 去掉金额数字中的逗号","
        str = str.replaceAll(",", "");
        String integerStr;//整数部分数字
        String decimalStr;//小数部分数字

        // 初始化：分离整数部分和小数部分
        if(str.indexOf(".")>0) {
            integerStr = str.substring(0,str.indexOf("."));
            decimalStr = str.substring(str.indexOf(".") + 1);
        }else if(str.indexOf(".")==0) {
            integerStr = "";
            decimalStr = str.substring(1);
        }else {
            integerStr = str;
            decimalStr = "";
        }

        // beyond超出计算能力，直接返回
        if(integerStr.length()>IUNIT.length) {
            return "超出计算能力！";
        }

        // 整数部分数字
        int[] integers = toIntArray(integerStr);
        // 判断整数部分是否存在输入012的情况
        if (integers.length>1 && integers[0] == 0) {
            return "抱歉，输入数字不符合要求！";
        }
        // 设置万单位
        boolean isWan = isWan5(integerStr);
        // 小数部分数字
        int[] decimals = toIntArray(decimalStr);
        // 返回最终的大写金额
        String result = getChineseInteger(integers, isWan) + getChineseDecimal(decimals);
        if(flag){
            // 如果是负数，加上"负"
            return "负" + result;
        }else{
            return result;
        }
    }

    /**
     *  将字符串转为int数组
     * @param number  数字
     * @return
     */
    private static int[] toIntArray(String number) {
        int[] array = new int[number.length()];
        for(int i = 0;i<number.length();i++) {
            array[i] = Integer.parseInt(number.substring(i,i+1));
        }
        return array;
    }

    /**
     *  将整数部分转为大写的金额
     * @param integers 整数部分数字
     * @param isWan  整数部分是否已经是达到【万】
     * @return
     */
    public static String getChineseInteger(int[] integers,boolean isWan) {
        StringBuffer chineseInteger = new StringBuffer("");
        int length = integers.length;
        if (length == 1 && integers[0] == 0) {
            return "";
        }
        for(int i=0; i<length; i++) {
            String key = "";
            if(integers[i] == 0) {
                if((length - i) == 13)//万（亿）
                    key = IUNIT[4];
                else if((length - i) == 9) {//亿
                    key = IUNIT[8];
                }else if((length - i) == 5 && isWan) {//万
                    key = IUNIT[4];
                }else if((length - i) == 1) {//元
                    key = IUNIT[0];
                }
                if((length - i)>1 && integers[i+1]!=0) {
                    key += NUMBERS[0];
                }
            }
            chineseInteger.append(integers[i]==0?key:(NUMBERS[integers[i]]+IUNIT[length - i -1]));
        }
        return chineseInteger.toString();
    }

    /**
     *  将小数部分转为大写的金额
     * @param decimals 小数部分的数字
     * @return
     */
    private static String getChineseDecimal(int[] decimals) {
        StringBuffer chineseDecimal = new StringBuffer("");
        for(int i = 0;i<decimals.length;i++) {
            if(i == 3) {
                break;
            }
            chineseDecimal.append(decimals[i]==0?"":(NUMBERS[decimals[i]]+DUNIT[i]));
        }
        return chineseDecimal.toString();
    }

    /**
     *  判断当前整数部分是否已经是达到【万】
     * @param integerStr  整数部分数字
     * @return
     */
    private static boolean isWan5(String integerStr) {
        int length = integerStr.length();
        if(length > 4) {
            String subInteger = "";
            if(length > 8) {
                subInteger = integerStr.substring(length- 8,length -4);
            }else {
                subInteger = integerStr.substring(0,length - 4);
            }
            return Integer.parseInt(subInteger) > 0;
        }else {
            return false;
        }
    }


    /**
     * 人民币转换为大写,格式为：x万x千x百x十x元x角x分
     * @param numberOfMoney
     * @return
     */
    public static String number2CNMontray(String numberOfMoney) {
        return number2CNMontray(new BigDecimal(numberOfMoney));
    }


    /**
     * 人民币转换为大写,格式为：x万x千x百x十x元x角x分
     * @param numberOfMoney 传入的金额
     * @return
     */
    public static String number2CNMontray(BigDecimal numberOfMoney) {
        StringBuffer sb = new StringBuffer();
        int signum = numberOfMoney.signum();
        // 零元整的情况
        if (signum == 0) {
            return CN_ZEOR_FULL;
        }
        //这里会进行金额的四舍五入
        long number = numberOfMoney.movePointRight(MONEY_PRECISION).setScale(0, 4).abs().longValue();
        // 得到小数点后两位值
        long scale = number % 100;
        int numUnit = 0;
        int numIndex = 0;
        boolean getZero = false;
        // 判断最后两位数，一共有四中情况：00 = 0, 01 = 1, 10, 11
        if (!(scale > 0)) {
            numIndex = 2;
            number = number / 100;
            getZero = true;
        }
        if ((scale > 0) && (!(scale % 10 > 0))) {
            numIndex = 1;
            number = number / 10;
            getZero = true;
        }
        int zeroSize = 0;
        while (true) {
            if (number <= 0) {
                break;
            }
            // 每次获取到最后一个数
            numUnit = (int) (number % 10);
            if (numUnit > 0) {
                if ((numIndex == 9) && (zeroSize >= 3)) {
                    sb.insert(0, CN_UPPER_MONETRAY_UNIT[6]);
                }
                if ((numIndex == 13) && (zeroSize >= 3)) {
                    sb.insert(0, CN_UPPER_MONETRAY_UNIT[10]);
                }
                sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
                sb.insert(0, CN_UPPER_NUMBER[numUnit]);
                getZero = false;
                zeroSize = 0;
            } else {
                ++zeroSize;
                if (!(getZero)) {
                    sb.insert(0, CN_UPPER_NUMBER[numUnit]);
                }
                if (numIndex == 2) {
                    if (number > 0) {
                        sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
                    }
                } else if (((numIndex - 2) % 4 == 0) && (number % 1000 > 0)) {
                    sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
                }
                getZero = true;
            }
            // 让number每次都去掉最后一个数
            number = number / 10;
            ++numIndex;
        }
        // 如果signum == -1，则说明输入的数字为负数，就在最前面追加特殊字符：负
        if (signum == -1) {
            sb.insert(0, CN_NEGATIVE);
        }
        // 输入的数字小数点后两位为"00"的情况，则要在最后追加特殊字符：整
        if (!(scale > 0)) {
            sb.append(CN_FULL);
        }
        return sb.toString();
    }


    /**
     * 将人民币转换为会计格式金额(xxxx,xxxx,xxxx.xx),保留两位小数
     * @param money 待转换的金额
     * @return 结果
     */
    public static String accountantMoney(BigDecimal money){
        return accountantMoney(money, 2, 1);
    }


    /**
     * 格式化金额，显示为xxx万元，xxx百万,xxx亿
     * @param money 待处理的金额
     * @param scale 小数点后保留的位数
     * @param divisor 格式化值（10:十元、100:百元,1000千元，10000万元......）
     * @return 结果
     */
    public static String getFormatMoney(BigDecimal money,int scale,double divisor){
        return formatMoney(money, scale, divisor) + getCellFormat(divisor);
    }


    /**
     * 获取会计格式的人民币(格式为:xxxx,xxxx,xxxx.xx)
     * @param money 待处理的金额
     * @param scale 小数点后保留的位数
     * @param divisor 格式化值（10:十元、100:百元,1000千元，10000万元......）
     * @return 结果
     */
    public static String getAccountantMoney(BigDecimal money, int scale, double divisor){
        return accountantMoney(money, scale, divisor) + getCellFormat(divisor);
    }


    /**
     * 将人民币转换为会计格式金额(xxxx,xxxx,xxxx.xx)
     * @param money 待处理的金额
     * @param scale 小数点后保留的位数
     * @param divisor 格式化值
     * @return 结果
     */
    private static String accountantMoney(BigDecimal money,int scale,double divisor){
        String disposeMoneyStr = formatMoney(money, scale, divisor);
        //小数点处理
        int dotPosition = disposeMoneyStr.indexOf(".");
        String exceptDotMoeny = null;//小数点之前的字符串
        String dotMeony = null;//小数点之后的字符串
        if(dotPosition > 0){
            exceptDotMoeny = disposeMoneyStr.substring(0,dotPosition);
            dotMeony = disposeMoneyStr.substring(dotPosition);
        }else{
            exceptDotMoeny = disposeMoneyStr;
        }
        //负数处理
        int negativePosition = exceptDotMoeny.indexOf("-");
        if(negativePosition == 0){
            exceptDotMoeny = exceptDotMoeny.substring(1);
        }
        StringBuffer reverseExceptDotMoney = new StringBuffer(exceptDotMoeny);
        reverseExceptDotMoney.reverse();//字符串倒转
        char[] moneyChar = reverseExceptDotMoney.toString().toCharArray();
        StringBuffer returnMeony = new StringBuffer();//返回值
        for(int i = 0; i < moneyChar.length; i++){
            if(i != 0 && i % 3 == 0){
                returnMeony.append(",");//每隔3位加','
            }
            returnMeony.append(moneyChar[i]);
        }
        returnMeony.reverse();//字符串倒转
        if(dotPosition > 0){
            returnMeony.append(dotMeony);
        }
        if(negativePosition == 0){
            return "-" + returnMeony.toString();
        }else{
            return returnMeony.toString();
        }
    }


    /**
     * 格式化金额，显示为xxx万元，xxx百万,xxx亿
     * @param money 待处理的金额
     * @param scale  小数点后保留的位数
     * @param divisor 格式化值
     * @return 结果
     */
    private static String formatMoney(BigDecimal money,int scale,double divisor){
        if (divisor == 0) {
            return "0.00";
        }
        if (scale < 0) {
            return "0.00";
        }
        BigDecimal divisorBD = new BigDecimal(divisor);
        return money.divide(divisorBD, scale, RoundingMode.HALF_UP).toString();
    }

    private static String getCellFormat(double divisor){
        String str = String.valueOf(divisor);
        int len = str.substring(0,str.indexOf(".")).length();
        String cell = "";
        switch(len){
            case 1:
                cell = "元";
                break;
            case 2:
                cell = "十元";
                break;
            case 3:
                cell = "百元";
                break;
            case 4:
                cell = "千元";
                break;
            case 5:
                cell = "万元";
                break;
            case 6:
                cell = "十万元";
                break;
            case 7:
                cell = "百万元";
                break;
            case 8:
                cell = "千万元";
                break;
            case 9:
                cell = "亿元";
                break;
            case 10:
                cell = "十亿元";
                break;
        }
        return cell;
    }

}
